#region License, Terms and Conditions
//
// Prawks - Copyright (c) 2007 Atif Aziz. All rights reserved.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 2.1 of the License, or (at your option)
// any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
// details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
//
#endregion

namespace Configuration
{
    #region Imports

    using System.Xml;

    using IEnumerator = System.Collections.IEnumerator;
    using Debug = System.Diagnostics.Debug;
    using ConfigurationException = System.Configuration.ConfigurationException;
    using ArrayList = System.Collections.ArrayList;

    #endregion

    internal sealed class SectionReader
    {
        private readonly IEnumerator _childEnumerator;
        private readonly bool _autoSkip;
        private readonly bool _autoValidateAttributes;
        private ArrayList _attributesRead;

        public SectionReader(XmlNode section) : this (section, true, true) {}

        public SectionReader(XmlNode section, bool autoValidateAttributes, bool autoSkip) 
        {
            Debug.Assert(section != null);

            _autoSkip = autoSkip;
            _autoValidateAttributes = autoValidateAttributes;
            _childEnumerator = section.ChildNodes.GetEnumerator();
        }

        public XmlNode CurrentNode
        {
            get { return (XmlNode) _childEnumerator.Current; }
        }

        public bool Read()
        {
            if (_attributesRead == null)
            {
                _attributesRead = new ArrayList();
            }
            else
            {
                if (_autoValidateAttributes)
                    ReportInvalidAttributes();

                _attributesRead.Clear();
            }

            if (!_childEnumerator.MoveNext())
                return false;

            if (_autoSkip)
            {
                while (CanSkipNode())
                {
                    if (!_childEnumerator.MoveNext())
                        return false;
                }
            }

            return true;
        }

        public bool CanSkipNode()
        {
            XmlNode node = CurrentNode;

            return node.NodeType == XmlNodeType.Comment ||
                   node.NodeType == XmlNodeType.Whitespace ||
                   node.NodeType == XmlNodeType.ProcessingInstruction;
        }

#pragma warning disable 618 // CS0618: 'System.Configuration.ConfigurationException.ConfigurationException(string)' is obsolete: 'This class is obsolete, to create a new exception create a System.Configuration!System.Configuration.ConfigurationErrorsException'

        public void ReportInvalidAttributes()
        {
            foreach (XmlAttribute attribute in CurrentNode.Attributes)
            {
                if (!_attributesRead.Contains(attribute))
                    throw new ConfigurationException(string.Format("Attribute '{0}' is not valid.", attribute.Name), CurrentNode);
            }
        }

        public void ValidateElement(string name, bool canHaveChildren)
        {
            XmlNode node = CurrentNode;

            if (node.NodeType != XmlNodeType.Element)
                throw new ConfigurationException("Invalid node type.", node);

            if (node.Name != name)
                throw new ConfigurationException(string.Format("'{0}' is not a recognized element.", node.Name), node);

            if (!canHaveChildren && node.HasChildNodes)
                throw new ConfigurationException(string.Format("'{0}' element cannot have child nodes.", node.Name), node);
        }

        public string GetStringAttribute(string name)
        {
            return GetStringAttribute(name, true, false);
        }

        public string GetStringAttribute(string name, bool required, bool canBeEmpty)
        {
            XmlNode node = ReadAttribute(name, required);

            if (!canBeEmpty && node.Value.Length == 0)
                throw new ConfigurationException(string.Format("Attribute '{0}' cannot be empty.", name), CurrentNode);
            
            return node.Value;
        }

        private XmlAttribute ReadAttribute(string name, bool required)
        {
            XmlAttribute attribute = (XmlAttribute) CurrentNode.Attributes.GetNamedItem(name);

            if (attribute == null && required)
                throw new ConfigurationException(string.Format("Required attribute '{0}' is missing.", name), CurrentNode);

            if (!_attributesRead.Contains(attribute))
                _attributesRead.Add(attribute);

            return attribute;
        }

#pragma warning restore 618
    
    }
}
